package com.markedquilt.comment.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.markedquilt.comment.coomon.BussinessException;
import com.markedquilt.comment.coomon.EmBussinessError;
import com.markedquilt.comment.dao.ShopModelMapper;
import com.markedquilt.comment.model.CategoryModel;
import com.markedquilt.comment.model.SellerModel;
import com.markedquilt.comment.model.ShopModel;
import com.markedquilt.comment.service.CategoryService;
import com.markedquilt.comment.service.SellerService;
import com.markedquilt.comment.service.ShopService;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.Response;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author ZhangKe
 * @Description
 * @date 2020/4/21 1:59.
 */
@Service
public class ShopServiceImpl implements ShopService {

    @Autowired
    private ShopModelMapper shopModelMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SellerService sellerService;

    @Autowired
    private RestHighLevelClient highLevelClient;

    @Override
    @Transactional
    public ShopModel create(ShopModel shopModel) throws BussinessException {
        shopModel.setCreatedAt(new Date());
        shopModel.setUpdatedAt(new Date());

        //校验商家是否存在正确
        SellerModel sellerModel=sellerService.get(shopModel.getSellerId());
        if (sellerModel == null){
            throw new BussinessException(EmBussinessError.PARAMETER_VALIDATION_ERROR,"商户不存在");

        }
        if (sellerModel.getDisabledFlag().intValue()==1){
            throw new BussinessException(EmBussinessError.PARAMETER_VALIDATION_ERROR,"商户已禁用");
        }

        //校验类目是否存在正确
        CategoryModel categoryModel = categoryService.get(shopModel.getCategoryId());
        if (categoryModel == null){
            throw new BussinessException(EmBussinessError.PARAMETER_VALIDATION_ERROR,"类目不存在");
        }
        shopModelMapper.insertSelective(shopModel);
        return get(shopModel.getId());
    }

    @Override
    public ShopModel get(Integer id) {
        ShopModel shopModel=shopModelMapper.selectByPrimaryKey(id);
        if (shopModel==null){
            return null;
        }
        shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
        shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));

        return shopModel;
    }

    @Override
    public List<ShopModel> selectAll() {
        List<ShopModel> shopModelList = shopModelMapper.selectAll();
        shopModelList.forEach(shopModel -> {
            shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
            shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
        });
        return shopModelList;
    }

    @Override
    public List<ShopModel> recommend(BigDecimal longitude, BigDecimal latitude) {
        List<ShopModel> shopModelList = shopModelMapper.recommend(longitude, latitude);
        shopModelList.forEach(shopModel -> {
            shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
            shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
        });
        return shopModelList;
    }

    @Override
    public List<Map<String, Object>> searchGroupByTags(String keyword, Integer categoryId, String tags) {
        return shopModelMapper.searchGroupByTags(keyword,categoryId,tags);
    }

    @Override
    public Integer CountAllShop() {
        return shopModelMapper.countAllShop();
    }

    @Override
    public List<ShopModel> search(BigDecimal longitude, BigDecimal latitude, String keyword,
                                  Integer orderby, Integer categoryId,String tags) {
        List<ShopModel> shopModelList = shopModelMapper.search(longitude, latitude, keyword,orderby,categoryId,tags);
        shopModelList.forEach(shopModel -> {
            shopModel.setSellerModel(sellerService.get(shopModel.getSellerId()));
            shopModel.setCategoryModel(categoryService.get(shopModel.getCategoryId()));
        });
        return shopModelList;
    }

    /**
     * 搜索v2.0-接入elasticsearch
     *
     */
    @Override
    public Map<String, Object> searchES(BigDecimal longitude, BigDecimal latitude, String keyword, Integer orderby, Integer categoryId, String tags) throws IOException {
        Map<String, Object> result=new HashMap<>();

        /** 注解的方式无法实现复杂的api查询 */
//        SearchRequest searchRequest=new SearchRequest("shop");  //传入索引名
//        SearchSourceBuilder searchSourceBuilder=new SearchSourceBuilder();
//        searchSourceBuilder.query(QueryBuilders.matchQuery("name",keyword));
//        searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
//        searchRequest.source(searchSourceBuilder);
//
//        SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//        SearchHit[] hits = searchResponse.getHits().getHits();
//        List<Integer> shopIdsList = new ArrayList<>();
//        for (SearchHit hit:hits) {
//            shopIdsList.add(new Integer(hit.getSourceAsMap().get("id").toString()));
//        }
        Request request=new Request("GET","/shop/_search");

        //构建请求
        JSONObject jsonRequestObject=new JSONObject();
        //构建source
        jsonRequestObject.put("_source","*");

        //构建自定义距离字段
        jsonRequestObject.put("script_fields",new JSONObject());
        jsonRequestObject.getJSONObject("script_fields").put("distance",new JSONObject());
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").put("script",new JSONObject());
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
                .put("source","haversin(lat,lon,doc['location'].lat,doc['location'].lon)");
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
                .put("lang","expression");
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
                .put("params",new JSONObject());
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
                .getJSONObject("params").put("lat",latitude);
        jsonRequestObject.getJSONObject("script_fields").getJSONObject("distance").getJSONObject("script")
                .getJSONObject("params").put("lon",longitude);

        //构建query
        jsonRequestObject.put("query",new JSONObject());
        //构建function score
        jsonRequestObject.getJSONObject("query").put("function_score",new JSONObject());
        //构建function score内的query
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("query",new JSONObject());
        //构建function score内的query内的bool
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").
                put("bool",new JSONObject());
        //构建function score内的query内的bool内的must
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").put("must",new JSONArray());
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").add(new JSONObject());
        //构建match query
        int queryIndex=0;
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).put("match",new JSONObject());//{"match": {"name": {"query": "凯悦","boost": 0.1}}},
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match")
                .put("name",new JSONObject());//{"name": {"query": "凯悦","boost": 0.1}}
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match")
                .getJSONObject("name").put("query",keyword);
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).getJSONObject("match")
                .getJSONObject("name").put("boost",0.1);


        queryIndex++;
        //构建第二个query---》term
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").add(new JSONObject());
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).put("term",new JSONObject());//{"term": {"seller_disabled_flag": 0}}
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query")
                .getJSONObject("bool").getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("seller_disabled_flag",0);//{"seller_disabled_flag": 0}

        if(tags != null){
            queryIndex++;
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").getJSONObject(queryIndex).put("term",new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("tags",tags);
        }

        if(categoryId != null){
            queryIndex++;
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").getJSONObject(queryIndex).put("term",new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONObject("query").getJSONObject("bool")
                    .getJSONArray("must").getJSONObject(queryIndex).getJSONObject("term").put("category_id",categoryId);
        }

        //构建functions部分
        jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("functions",new JSONArray());

        int functionIndex = 0;
        if(orderby == null) {
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("gauss", new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss").put("location", new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                    .getJSONObject("location").put("origin", latitude.toString() + "," + longitude.toString());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                    .getJSONObject("location").put("scale", "100km");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                    .getJSONObject("location").put("offset", "0km");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("gauss")
                    .getJSONObject("location").put("decay", "0.5");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 9);

            functionIndex++;
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor", new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                    .put("field", "remark_score");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 0.2);

            functionIndex++;
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor", new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                    .put("field", "seller_remark_score");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("weight", 0.1);


            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("score_mode", "sum");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("boost_mode", "sum");
        }else{
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").add(new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).put("field_value_factor",new JSONObject());
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").getJSONArray("functions").getJSONObject(functionIndex).getJSONObject("field_value_factor")
                    .put("field","price_per_man");

            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("score_mode","sum");
            jsonRequestObject.getJSONObject("query").getJSONObject("function_score").put("boost_mode","replace");
        }


        //构建排序字段
        jsonRequestObject.put("sort",new JSONArray());
        jsonRequestObject.getJSONArray("sort").add(new JSONObject());
        jsonRequestObject.getJSONArray("sort").getJSONObject(0).put("_score",new JSONObject());
        if(orderby == null){
            jsonRequestObject.getJSONArray("sort").getJSONObject(0).getJSONObject("_score").put("order","desc");
        }else{
            jsonRequestObject.getJSONArray("sort").getJSONObject(0).getJSONObject("_score").put("order","asc");
        }

        //聚合字段
        jsonRequestObject.put("aggs",new JSONObject());
        jsonRequestObject.getJSONObject("aggs").put("group_by_tags",new JSONObject());
        jsonRequestObject.getJSONObject("aggs").getJSONObject("group_by_tags").put("terms",new JSONObject());
        jsonRequestObject.getJSONObject("aggs").getJSONObject("group_by_tags").getJSONObject("terms").put("field","tags");

        String reqJson = jsonRequestObject.toJSONString();


        System.out.println(reqJson);
        request.setJsonEntity(reqJson);
        Response response = highLevelClient.getLowLevelClient().performRequest(request);
        String responseStr = EntityUtils.toString(response.getEntity());
        System.out.println(responseStr);
        JSONObject jsonObject = JSONObject.parseObject(responseStr);
        JSONArray jsonArr = jsonObject.getJSONObject("hits").getJSONArray("hits");
        List<ShopModel> shopModelList= new ArrayList<>();
        for (int i = 0; i < jsonArr.size(); i++) {
            JSONObject jsonObj = jsonArr.getJSONObject(i);
            Integer id = new Integer(jsonObj.get("_id").toString());
            BigDecimal distance=new BigDecimal(jsonObj.getJSONObject("fields").getJSONArray("distance").get(0).toString());
            ShopModel shopModel=get(id);
            shopModel.setDistance(distance.multiply(new BigDecimal(1000).setScale(0,BigDecimal.ROUND_CEILING)).intValue());
            shopModelList.add(shopModel);
        }

        List<Map> tagsList = new ArrayList<>();
        JSONArray tagsJsonArray = jsonObject.getJSONObject("aggregations").getJSONObject("group_by_tags").getJSONArray("buckets");
        for(int i = 0; i < tagsJsonArray.size();i++){
            JSONObject jsonObj = tagsJsonArray.getJSONObject(i);
            Map<String,Object> tagMap = new HashMap<>();
            tagMap.put("tags",jsonObj.getString("key"));
            tagMap.put("num",jsonObj.getInteger("doc_count"));
            tagsList.add(tagMap);
        }
        result.put("tags",tagsList);

        result.put("shop",shopModelList);
        return result;
    }
}
